package com.swensen.game.data;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;

public class FillTile extends Tile {

	public enum Neighbor {
		EAST,
		NORTH,
		NORTH_EAST,
		NORTH_WEST,
		SOUTH,
		SOUTH_EAST,
		SOUTH_WEST,
		WEST;
	}
	
	private static final EnumSet<Neighbor> ALL_NEIGHBORS =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.NORTH_WEST, Neighbor.SOUTH, Neighbor.SOUTH_EAST,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_NORTH_EAST = 
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH,
					Neighbor.NORTH_WEST, Neighbor.SOUTH, Neighbor.SOUTH_EAST,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_NORTH_WEST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.SOUTH, Neighbor.SOUTH_EAST,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_SOUTH_EAST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.NORTH_WEST, Neighbor.SOUTH,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_SOUTH_WEST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.NORTH_WEST, Neighbor.SOUTH, Neighbor.SOUTH_EAST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_EAST_CORNERS = 
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH,
					Neighbor.NORTH_WEST, Neighbor.SOUTH,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_NORTH_CORNERS =
			EnumSet.of(Neighbor.EAST, Neighbor.NORTH,
					Neighbor.SOUTH, Neighbor.SOUTH_EAST, Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_SOUTH_CORNERS =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.NORTH_WEST, Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_WEST_CORNERS =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.SOUTH, Neighbor.SOUTH_EAST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_EAST =
			EnumSet.<Neighbor>of(Neighbor.NORTH,
					Neighbor.NORTH_WEST, Neighbor.SOUTH,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_NORTH =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.SOUTH, Neighbor.SOUTH_EAST,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_SOUTH =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.NORTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_WEST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.SOUTH, Neighbor.SOUTH_EAST);
	
	private static final EnumSet<Neighbor> NO_CORNERS =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_CORNERS_BUT_NORTH_EAST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST,
					Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_CORNERS_BUT_NORTH_WEST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH,
					Neighbor.NORTH_WEST, Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_CORNERS_BUT_SOUTH_EAST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, 
					Neighbor.SOUTH, Neighbor.SOUTH_EAST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> NO_CORNERS_BUT_SOUTH_WEST =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, 
					Neighbor.SOUTH, Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_N_W_NW =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH,
					Neighbor.NORTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_N_W_NE =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> N_S_W_NW =
			EnumSet.<Neighbor>of(Neighbor.NORTH, Neighbor.NORTH_WEST, Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> N_S_W_SW =
			EnumSet.<Neighbor>of(Neighbor.NORTH, Neighbor.SOUTH,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_S_W_SW =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.SOUTH,
					Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_S_W_SE =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.SOUTH, Neighbor.SOUTH_EAST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_N_S_NE =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST, Neighbor.SOUTH);
	
	private static final EnumSet<Neighbor> E_N_S_SE =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.SOUTH, Neighbor.SOUTH_EAST);
	
	private static final EnumSet<Neighbor> E_N_S =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.SOUTH);
	
	private static final EnumSet<Neighbor> E_N_W =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_S_W =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> N_S_W =
			EnumSet.<Neighbor>of(Neighbor.NORTH, Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_N_NE =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH, Neighbor.NORTH_EAST);
	
	private static final EnumSet<Neighbor> E_S_SE =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.SOUTH, Neighbor.SOUTH_EAST);
	
	private static final EnumSet<Neighbor> N_W_NW =
			EnumSet.<Neighbor>of(Neighbor.NORTH, Neighbor.NORTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> S_W_SW =
			EnumSet.<Neighbor>of(Neighbor.SOUTH, Neighbor.SOUTH_WEST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_N =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.NORTH);
	
	private static final EnumSet<Neighbor> E_S =
			EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.SOUTH);
	
	private static final EnumSet<Neighbor> N_W =
			EnumSet.<Neighbor>of(Neighbor.NORTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> S_W =
			EnumSet.<Neighbor>of(Neighbor.SOUTH, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> E_W = EnumSet.<Neighbor>of(Neighbor.EAST, Neighbor.WEST);
	
	private static final EnumSet<Neighbor> N_S = EnumSet.<Neighbor>of(Neighbor.NORTH, Neighbor.SOUTH);
	
	private List<BasicTile> tiles;
	private EnumSet<Neighbor> neighbors;
	private int tileNumber = 3;
	
	public FillTile(TileId id, List<BasicTile> tiles) {
		super(id, TileType.FILL);
		if (tiles == null || tiles.size() >= 48) {
			throw new InvalidParameterException("Fill tiles require 39 images for use");
		}
		this.tiles = tiles;
		neighbors = EnumSet.noneOf(Neighbor.class);
	}
	
	public void addNeighbor(Neighbor neighbor) {
		neighbors.add(neighbor);
		tileNumber = getTileIndex();
	}
	
	public void removeNeighbor(Neighbor neighbor) {
		neighbors.remove(neighbor);
		tileNumber = getTileIndex();
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = -6586060231436393047L;

	@Override
	public TileId getImageData() {
		return tiles.get(tileNumber).getImageData();
	}

	private int getTileIndex() {
		// All neighbors
		if (neighbors.containsAll(ALL_NEIGHBORS)) {
			return 5;
		}
		
		// All but one
		if (neighbors.containsAll(NO_NORTH_EAST)) {
			return 33;
		}
		if (neighbors.containsAll(NO_NORTH_WEST)) {
			return 32;
		}
		if (neighbors.containsAll(NO_SOUTH_EAST)) {
			return 34;
		}
		if (neighbors.containsAll(NO_SOUTH_WEST)) {
			return 31;
		}
		
		// All but two
		if (neighbors.containsAll(NO_NORTH_CORNERS)) {
			return 35;
		}
		if (neighbors.containsAll(NO_EAST_CORNERS)) {
			return 38;
		}
		if (neighbors.containsAll(NO_SOUTH_CORNERS)) {
			return 37;
		}
		if (neighbors.containsAll(NO_WEST_CORNERS)) {
			return 36;
		}
		
		// All but three
		if (neighbors.containsAll(NO_NORTH)) {
			return 1;
		}
		if (neighbors.containsAll(NO_EAST)) {
			return 6;
		}
		if (neighbors.containsAll(NO_SOUTH)) {
			return 9;
		}
		if (neighbors.containsAll(NO_WEST)) {
			return 4;
		}
		
		if (neighbors.containsAll(NO_CORNERS_BUT_SOUTH_WEST)) {
			return 27;
		}
		if (neighbors.containsAll(NO_CORNERS_BUT_NORTH_WEST)) {
			return 28;
		}
		if (neighbors.containsAll(NO_CORNERS_BUT_NORTH_EAST)) {
			return 23;
		}
		if (neighbors.containsAll(NO_CORNERS_BUT_SOUTH_EAST)) {
			return 19;
		}
		
		// All but four
		if (neighbors.containsAll(E_N_W_NW)) {
			return 39;
		}
		if (neighbors.containsAll(E_N_W_NE)) {
			return 40;
		}
		if (neighbors.containsAll(N_S_W_SW)) {
			return 41;
		}
		if (neighbors.containsAll(N_S_W_NW)) {
			return 42;
		}
		if (neighbors.containsAll(E_S_W_SE)) {
			return 43;
		}
		if (neighbors.containsAll(E_S_W_SW)) {
			return 44;
		}
		if (neighbors.containsAll(E_N_S_NE)) {
			return 45;
		}
		if (neighbors.containsAll(E_N_S_SE)) {
			return 46;
		}
		
		if (neighbors.containsAll(NO_CORNERS)){
			return 21;
		}
		
		// Only three neighbors
		if (neighbors.containsAll(E_S_W)) {
			return 17;
		}
		if (neighbors.containsAll(N_S_W)) {
			return 22;
		}
		if (neighbors.containsAll(E_N_S)) {
			return 20;
		}
		if (neighbors.containsAll(E_N_W)) {
			return 25;
		}
		
		if (neighbors.containsAll(E_S_SE)) {
			return 0;
		}
		if (neighbors.containsAll(S_W_SW)) {
			return 2;
		}
		if (neighbors.containsAll(E_N_NE)) {
			return 8;
		}
		if (neighbors.containsAll(N_W_NW)) {
			return 10;
		}
		
		// Only two neighbors
		
		if (neighbors.containsAll(E_N)) {
			return 20;
		}
		if (neighbors.containsAll(E_S)) {
			return 12;
		}
		if (neighbors.containsAll(E_W)) {
			return 7;
		}
		if (neighbors.containsAll(N_S)) {
			return 11;
		}
		if (neighbors.containsAll(N_W)) {
			return 22;
		}
		if (neighbors.containsAll(S_W)) {
			return 14;
		}
		
		// Only one neighbor
		if (neighbors.contains(Neighbor.EAST)) {
			return 38;
		}
		if (neighbors.contains(Neighbor.NORTH)) {
			return 35;
		}
		if (neighbors.contains(Neighbor.SOUTH)) {
			return 37;
		}
		if (neighbors.contains(Neighbor.WEST)) {
			return 36;
		}
		
		return 3;
	}

	@Override
	public TileId getStillImageData() {
		return tiles.get(3).getImageData();
	}

	@Override
	public FillTile copy() {
//		List<BasicTile> deepCopy = new ArrayList<BasicTile>();
//		for (BasicTile tile : tiles) {
//			deepCopy.add(tile.copy());
//		}
		return new FillTile(id, tiles);
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null || id.getFileName() == null) ? 0 : id.getFileName().hashCode());
		return result;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Tile other = (Tile) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (id.getFileName() == null) {
			if (other.id.getFileName() != null)
				return false;
		} else if (!id.getFileName().equals(other.id.getFileName()))
			return false;
		return true;
	}

}
